/*
* FileName BitmapImage.cpp
*/

#include "stdafx.h"

#include "glib/glib-2.0/glib.h"
#include "cairo/cairo-win32.h"
#include <fcntl.h>
#include <Windows.h>
#include "FreeImage/FreeImage.h"

#include "BitmapImage.h"
#include "unistd.h"
#include "MoonPixelBuffer.h"

#define MULTI_THREAD_LOAD_TILE

namespace DeepZoomImage
{

#ifdef WORDS_BIGENDIAN
#define set_pixel_bgra(pixel,index,b,g,r,a) \
	G_STMT_START { \
	((unsigned char *)(pixel))[index]   = a; \
	((unsigned char *)(pixel))[index+1] = r; \
	((unsigned char *)(pixel))[index+2] = g; \
	((unsigned char *)(pixel))[index+3] = b; \
} G_STMT_END
#define get_pixel_bgr_p(p,b,g,r) \
	G_STMT_START { \
	r = *(p);   \
	g = *(p+1); \
	b = *(p+2); \
} G_STMT_END
#else
#define set_pixel_bgra(pixel,index,b,g,r,a) \
	G_STMT_START { \
	((unsigned char *)(pixel))[index]   = b; \
	((unsigned char *)(pixel))[index+1] = g; \
	((unsigned char *)(pixel))[index+2] = r; \
	((unsigned char *)(pixel))[index+3] = a; \
} G_STMT_END
#define get_pixel_bgr_p(p,b,g,r) \
	G_STMT_START { \
	b = *(p);   \
	g = *(p+1); \
	r = *(p+2); \
} G_STMT_END
#endif
#define get_pixel_bgra(color, b, g, r, a) \
	G_STMT_START { \
	a = *(p+3);	\
	r = *(p+2);	\
	g = *(p+1);	\
	b = *(p+0);	\
} G_STMT_END

#include "alpha-premul-table.inc"

	//
	// Expands RGB to ARGB allocating new buffer for it.
	//
	static gpointer expand_rgb_to_argb (MoonPixbuf *pixbuf)
	{
		guchar *pb_pixels = pixbuf->GetPixels ();
		guchar *p;
		int w = pixbuf->GetWidth ();
		int h = pixbuf->GetHeight ();
		int stride = w * 4;
		guchar *data = (guchar *) g_malloc (stride * h);
		guchar *out;

		for (int y = 0; y < h; y ++) {
			p = pb_pixels + y * pixbuf->GetRowStride ();
			//out = data + y * (stride);
			out = (data + stride*h)- (y+1) * (stride);
			for (int x = 0; x < w; x ++) {
				guchar r, g, b;

				get_pixel_bgr_p (p, b, g, r);
				//set_pixel_bgra (out, 0, r, g, b, 255);
				set_pixel_bgra (out, 0, b, g, r, 255);
				p += 3;
				out += 4;
			}
		}

		return (gpointer) data;
	}

	//
	// Converts RGBA unmultiplied alpha to ARGB pre-multiplied alpha.
	//
	static gpointer premultiply_rgba (MoonPixbuf *pixbuf)
	{
		guchar *pb_pixels = pixbuf->GetPixels ();
		guchar *p;
		int w = pixbuf->GetWidth ();
		int h = pixbuf->GetHeight ();
		int stride = w * 4;
		guchar *data = (guchar *) g_malloc (stride * h);
		guchar *out;

		for (int y = 0; y < h; y ++) {
			p = pb_pixels + y * pixbuf->GetRowStride ();
			out = data + y * (stride);
			for (int x = 0; x < w; x ++) {
				guchar r, g, b, a;

				get_pixel_bgra (p, b, g, r, a);

				/* pre-multipled alpha */
				if (a == 0) {
					r = g = b = 0;
				}
				else if (a < 255) {
					r = pre_multiplied_table [r][a];
					g = pre_multiplied_table [g][a];
					b = pre_multiplied_table [b][a];
				}

				/* store it back, swapping red and blue */
				set_pixel_bgra (out, 0, r, g, b, a);

				p += 4;
				out += 4;
			}
		}

		return (gpointer) data;
	}

	static gpointer expand_bgr_to_bgra (unsigned char* buffer,int width,int height,int imstride)
	{
		//guchar *pb_pixels = pixbuf->GetPixels ();

		guchar *p;
		int w = width;
		int h = height;
		int stride = w * 4;
		guchar *data = (guchar *) g_malloc (stride * h);
		guchar *out;

		for (int y = 0; y < h; y ++) {
			p = buffer + y * imstride;
			//out = data + y * (stride);
			out = (data + stride*h)- (y+1) * (stride);
			for (int x = 0; x < w; x ++) {
				guchar r, g, b;

				get_pixel_bgr_p (p, b, g, r);
				//set_pixel_bgra (out, 0, r, g, b, 255);
				set_pixel_bgra (out, 0, b, g, r, 255);
				p += 3;
				out += 4;
			}
		}

		return (gpointer) data;
	}

	BitmapImage::BitmapImage ()		
	: part_name(NULL)
	{
		FreeImage_Initialise(true);
	}

	BitmapImage::~BitmapImage ()
	{
		FreeImage_DeInitialise();

 		if ( part_name )
 		{
 			delete part_name;
 			part_name = NULL;
 		}
	}

	void BitmapImage::loaderComplete ()
	{
		ImageFileFormatChange( part_name,"jpg");
		//PixmapComplete ();
		return;

#if 0
 		if ( loader == NULL) 
 		{
 			char *filename = part_name;//downloader->GetDownloadedFilename (part_name);
 
 			guchar b[4096];
 			int offset = 0;
 			ssize_t n;
 			int fd;
 
 			if ((fd = open(filename, O_RDONLY)) == -1)
 			{
 				//moon_error = new MoonError (MoonError::EXCEPTION, 4001, "failed to open file");
 				goto failed;
 			}
 
 			do {
 				do {
 					n = read (fd, b, sizeof (b));
 				} while (n == -1/* && errno == EINTR*/);
 
 				if (n == -1) break;
 
 				ImageFileFormatChange(filename,"jpg");
 				PixbufWrite (b, offset, n);
 
 				offset += n;
 			} while (n > 0/* && !moon_error*/);
 
 			close (fd);
 		}
#endif
	}

	void BitmapImage::SetTileSource (const void* value)
	{
		char * name = (char *)value;

 		if ( part_name && strcmp( name, part_name) !=0 )
 		{
 			delete part_name;
 		}
 		
 		int size = strlen(name);
 		part_name = new char [size+1];
 		memset( part_name, '\0', size+1 );
 		strcpy_s( part_name, size+1, name );
	
#ifdef MULTI_THREAD_LOAD_TILE
#else
		if (part_name)
		{
			loaderComplete();
		}
#endif
	}

	const void* BitmapImage::GetTileSource ()
	{
		return (void *)part_name;
	}

	bool BitmapImage::ImageFileFormatChange(const char* inFilePath,const char* inimgExt)
	{
		int inflag=BMP_DEFAULT;
		FREE_IMAGE_FORMAT infif=FIF_BMP;

		if (!strcmp(inimgExt,"bmp"))
		{
			infif=FIF_BMP;
			inflag=BMP_DEFAULT;
		}
		else if (!strcmp(inimgExt,"jpg"))
		{
			infif =FIF_JPEG;
			inflag = JPEG_DEFAULT;
		}
		else if (!strcmp(inimgExt,"png"))
		{
			infif =FIF_PNG;
			inflag = PNG_DEFAULT;
		}
		else
			return false;

		infif = FreeImage_GetFIFFromFilename(inFilePath);  

		if(infif != FIF_UNKNOWN && FreeImage_FIFSupportsReading(infif))  
		{  
			FIBITMAP * imageHandle;
			imageHandle = FreeImage_Load(infif,inFilePath,inflag);

			BYTE* bits = FreeImage_GetBits(imageHandle);
			m_PixelWidth = FreeImage_GetWidth(imageHandle);
			m_PixelHeight = FreeImage_GetHeight(imageHandle);
			imageStride = FreeImage_GetPitch(imageHandle);

			SetBitmapData ((gpointer)expand_bgr_to_bgra(bits,m_PixelWidth,m_PixelHeight,imageStride), true);

			//SetBitmapData ((gpointer)bits, true);

			FreeImage_Unload(imageHandle);

			Invalidate ();
		} else {
			return false;
		}

		return true;
	}
}